package cn.apijson.querycondition.query.template;


import cn.apijson.querycondition.query.conditions.ApiJsonPageInfo;
import cn.apijson.querycondition.query.conditions.ApiJsonQuery;
import cn.apijson.querycondition.query.dao.DwQueryDao;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>查询模板工具</p>
 *
 * @author yingsheng.ye
 * @version 1.0.0
 * @since 2023/6/20 5:46
 */
@Slf4j
public class ApiJsonQueryTemplate {

    /**
     * 数仓查询接口
     */
    private DwQueryDao dao;

    /**
     * 设置查询数仓实现
     *
     * @param dao DwQueryDao
     */
    public void setDao(DwQueryDao dao) {
        this.dao = dao;
    }

    /**
     * 查询列表服务
     *
     * @param query  查询条件
     * @param tClass 结果转换类
     * @param <T>    泛型
     * @return List<T>
     */
    public <T> List<T> getList(ApiJsonQuery query, Class<T> tClass) {
        return queryList(query, tClass);
    }

    /**
     * 查询列表服务
     *
     * @param query         查询条件
     * @param typeReference 结果转换类
     * @param <T>           泛型
     * @return List<T>
     */
    public <T> List<T> getList(ApiJsonQuery query, TypeReference<T> typeReference) {
        return queryList(query, typeReference);
    }

    /**
     * 查询列表服务
     *
     * @param query 查询条件
     * @param o     结果转换类
     * @param <T>   泛型
     * @return List<T>
     */
    private <T> List<T> queryList(ApiJsonQuery query, Object o) {
        //数仓返回单次有最大限制，所以需要循环分次获取
        List<T> result = new ArrayList<>();
        int limit = 2000;
        int page = 1;
        loopQueryPage(result, page, limit, query, o);
        return result;
    }


    /**
     * 查询列表
     *
     * @param result 查询结果
     * @param page   当前页
     * @param limit  分页大小
     * @param query  查询条件
     * @param o      返回类型
     * @param <T>    泛型
     */
    public <T> void loopQueryPage(List<T> result, int page, int limit, ApiJsonQuery query, Object o) {
        ApiJsonPageInfo<T> dwPageInfo = queryPageList(query, page, limit, o);
        result.addAll(dwPageInfo.getData());
        if ((long) page * limit < dwPageInfo.getTotal()) {
            //循环调用
            loopQueryPage(result, ++page, limit, query, o);
        }
    }

    /**
     * 查询一条数据
     *
     * @param query  查询条件
     * @param tClass 结果转换类
     * @param <T>    泛型
     * @return T
     */
    public <T> T getOne(ApiJsonQuery query, Class<T> tClass) {
        List<T> list = queryList(query, tClass);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        Assert.isTrue(list.size() == 1, "期望返回一条数据，但是结果返回多条数据");
        return list.get(0);
    }

    /**
     * 查询一条数据
     *
     * @param query         查询条件
     * @param typeReference 结果转换类
     * @param <T>           泛型
     * @return T
     */
    public <T> T getOne(ApiJsonQuery query, TypeReference<T> typeReference) {
        List<T> list = queryList(query, typeReference);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        Assert.isTrue(list.size() == 1, "期望返回一条数据，但是结果返回多条数据");
        return list.get(0);
    }


    /**
     * 查询统计数量
     *
     * @param query 查询条件
     * @return 条数
     */
    public Long count(ApiJsonQuery query) {
        Map<String, Object> params = new HashMap<>(16);
        params.put("@schema", query.getSchema());
        params.put("total@", "/[]/total");
        Map<String, Object> modelParams = new HashMap<>(16);
        modelParams.put(query.getTable(), query.getApiJsonParam());
        //查询表中的所有数据
        modelParams.put("query", 1);
        params.put("[]", modelParams);
        JSONObject data = resultToObject(dao.getData(params, query.getBiSigns()));
        return data.getLong("total");
    }

    /**
     * 分页查询结果
     *
     * @param query  查询条件
     * @param page   当前页
     * @param limit  分页条数
     * @param tClass 转换类
     * @param <T>    泛型
     * @return DwPageInfo<T>
     */
    public <T> ApiJsonPageInfo<T> getPageList(ApiJsonQuery query, Integer page, Integer limit, Class<T> tClass) {
        return queryPageList(query, page, limit, tClass);
    }

    /**
     * 分页查询结果
     *
     * @param query         查询条件
     * @param page          当前页
     * @param limit         分页条数
     * @param typeReference 转换类
     * @param <T>           泛型
     * @return DwPageInfo<T>
     */
    public <T> ApiJsonPageInfo<T> getPageList(ApiJsonQuery query, Integer page, Integer limit, TypeReference<T> typeReference) {
        return queryPageList(query, page, limit, typeReference);
    }

    /**
     * 分页查询结果
     *
     * @param query 查询条件
     * @param page  当前页
     * @param limit 分页条数
     * @param o     转换类
     * @param <T>   泛型
     * @return DwPageInfo<T>
     */
    private <T> ApiJsonPageInfo<T> queryPageList(ApiJsonQuery query, Integer page, Integer limit, Object o) {
        if (null == page || page < 1) {
            page = 1;
        }
        if (null == limit || limit < 1) {
            limit = 10;
        }
        Map<String, Object> params = new HashMap<>(16);
        params.put("@schema", query.getSchema());
        params.put("total@", "/[]/total");
        Map<String, Object> modelParams = new HashMap<>(16);
        modelParams.put(query.getTable(), query.getApiJsonParam());
        //apiJson分页从0开始
        int currentPage = page - 1;
        //查询表中的所有数据
        modelParams.put("page", currentPage);
        modelParams.put("count", limit);
        modelParams.put("query", 2);
        params.put("[]", modelParams);
        JSONObject data = resultToObject(dao.getData(params, query.getBiSigns()));
        ApiJsonPageInfo<T> dwPageInfo = new ApiJsonPageInfo<>();
        dwPageInfo.setData(praseResultToList(query.getTable(), data, o));
        dwPageInfo.setPage(page);
        dwPageInfo.setLimit(limit);
        dwPageInfo.setTotal(data.getLong("total"));
        return dwPageInfo;
    }


    /**
     * 结果转换
     *
     * @param res 返回结果
     * @return JSONObject
     */
    private JSONObject resultToObject(String res) {
        return JSON.parseObject(res);
    }

    /**
     * 将结果转换为list
     *
     * @param table 表名
     * @param data  数据
     * @param o     类型
     * @param <T>   Class<T>
     * @return List<T>
     */
    @SuppressWarnings("unchecked")
    private <T> List<T> praseResultToList(String table, JSONObject data, Object o) {
        //解析结果转义
        JSONArray jsonArray = data.getJSONArray("[]");
        List<T> list = new ArrayList<>();
        if (!CollectionUtils.isEmpty(jsonArray)) {
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject resultObj = jsonArray.getJSONObject(i).getJSONObject(table);
                //将结果转换成对象
                if (o instanceof TypeReference) {
                    TypeReference<T> tTypeReference = (TypeReference<T>) o;
                    list.add(JSON.parseObject(JSON.toJSONString(resultObj), tTypeReference));
                } else {
                    Class<T> tClass = (Class<T>) o;
                    list.add(JSON.parseObject(JSON.toJSONString(resultObj), tClass));
                }
            }
        }
        return list;
    }
}
